<?php
/**
 * Created by 项目.
 * User: xuguopeng
 * Date: 2019-12-31
 * Time: 16:29
 */

namespace app\api\service;

use app\api\model\MdbUser as UserModel;
use app\api\model\WxPic;
use app\api\model\WxReply;
use app\api\model\WxText;
use think\facade\Cache;
use think\facade\Request;

class Wechat
{
    private $token;
    private $wxAppId;
    private $wxAppSecret;
    private $isValid;
    private $param;
    private $msgObj;
    private $msgType;
    private $eventType;
    private $wxAccessUrl;

    function __construct()
    {
        $this->wxAppId = config('wechat.app_id');
        $this->wxAppSecret = config('wechat.app_secret');
        $this->token = config('wechat.token');
        $this->isValid = config('wechat.is_valid');
        $this->wxAccessUrl = sprintf(config('wechat.token_url'),
            $this->wxAppId,$this->wxAppSecret);
    }

    /**
     * 入口
     */
    public function get()
    {
        $param = Request::get();
        $this->param = $param;
        if ($this->isValid)
        {
            $this->valid ();
        }
        $this->getMsg ();
        $this->responseMsg ();
    }

    /**
     * 初次效验
     */
    private function valid()
    {
        $echoStr = $this->param['echostr'];
        if ($this->checkSignature ())
        {
            echo $echoStr;
            exit ();
        }
    }

    /**
     * 效验签名
     */
    private function checkSignature()
    {
        $signature = $this->param["signature"];
        $timestamp = $this->param["timestamp"];
        $nonce = $this->param["nonce"];
        $token = $this->token;
        $tmpArr = array (
            $token,
            $timestamp,
            $nonce
        );
        sort ( $tmpArr, SORT_STRING );
        $tmpStr = implode ( $tmpArr );
        $tmpStr = sha1 ( $tmpStr );
        return ($tmpStr == $signature) ? true : false;
    }

    /**
     * 获取消息
     */
    private function getMsg()
    {
        //验证消息的真实性
        if(!$this->checkSignature())
        {
            exit();
        }
        // 接收消息
        $postStr = file_get_contents("php://input");
        if (! empty ( $postStr ))
        {
            $this->msgObj = simplexml_load_string ( $postStr, 'SimpleXMLElement', LIBXML_NOCDATA );
            $this->msgType = strtolower ( $this->msgObj->MsgType );
        } else {
            $this->msgObj = null;
        }
    }

    /**
     * 回复消息
     */
    private function responseMsg()
    {
        switch ($this->msgType)
        {
            case 'text':
                $this->getData();
                break;
            case 'event':
                $this->eventOpt();
                break;
            default:
                break;
        }
    }

    /**
     * 事件处理
     */
    private function eventOpt()
    {
        $this->eventType = strtolower ( $this->msgObj->Event );
        switch ($this->eventType)
        {
            case 'subscribe':
                //未关注，判断用户信息并添加
                $this->checkMember();
                break;
            case 'unsubscribe':
                //取消关注修改用户状态
                $this->unStatus();
                break;
            case 'SCAN':
                break;
            case 'click':
                $this->menuClick();
                break;
            default:
                break;
        }
    }

    /**
     * 取消关注没有上级删除用户信息(除了第一个人)
     */
    private function unStatus()
    {
        $openId =  $this->msgObj->FromUserName;
        $userInfo = UserModel::where('openid','=',$openId)->find();
        if($userInfo)
        {
            // 取消关注操作
            UserModel::where('openid','=',$openId)->update(['subscribe'=>2]);
        }
    }

    /**
     * 自定义菜单事件处理
     */
    private function menuClick() {
        $this->eventkey = $this->msgObj->EventKey;
        $keyValue = $this->eventkey;
        $info = WxReply::where('title','=',$keyValue)->find();
        if ($info) {
            $this->responsedata ( $info ); // 接受的数据如果存在则回复相关数据
        } else {
            // 如果接受数据不存在则回复相关数据
            $this->isStatus ( 2 );
        }
    }
    /**
     * 判断是否首次关注
     */
    private function checkMember()
    {
        $openId =  $this->msgObj->FromUserName;
        $userInfo = UserModel::where('openid','=',$openId)->find();
        if($userInfo)
        {
            $token = Cache::get('wechat_access_token');
            if(!$token)
            {
                $access_token = $this->getAccessToken();
            }
            else{
                $access_token = $token;
            }
            $userInfo = $this->getUserInfo($access_token,$openId);
            UserModel::where('openid','=',$openId)->update(['subscribe'=>1]);
        }
        else{
            $this->createUser($openId);
        }
        $this->isStatus ( 1 );
    }
    /**
     * 获取回复数据并判断数据是否有对应的值
     */
    private function getData()
    {
        $data = $this->msgObj->Content;
        $info = WxReply::where('title','=',$data)
                ->where('status', '=' , 4)
                ->find();
        if ($info)
        {
            $this->responseData ( $info ); // 接受的数据如果存在则回复相关数据
        }
        else{
            // 如果接受数据不存在则回复相关数据
            $this->isStatus ( 2 );
        }
    }

    /**
     * 查询没有关键字回复的
     */
    private function isStatus($i)
    {
        switch ($i)
        {
            case 1 :
                $info = WxReply::where('status','in','1,3')->find();
                break;
            case 2 :
                $info = WxReply::where('status','in','2,3')->find();
                break;
            case 3 :
                $info = WxReply::where('status','=','3')->find();
                break;
        }
        $this->responseData ( $info );
    }
    /**
     * 读取要回复的数据
     */
    private function responseData($info)
    {
        $type = $info ['type'];
        $id = $info ['id'];
        if($type == 1)
        {
            //回复文本
            $data = WxText::where('code','=',$id)->find();
        }
        else{
            //回复图文
            $data = WxPic::with('img')->where('code','=',$id)->find();
        }
        $this->isResponseMsgType ( $type, $data );
    }

    /**
     * 判断要发送数据的类型并发送
     */
    private function isResponseMsgType($id, $data)
    {
        switch ($id)
        {
            case 1 :
                $this->textMsg ( $data['content'] );
                break;
            case 2 :
                $this->picMsg ( $data );
                break;
        }
    }

    /**
     * 回复文本消息
     */
    private function textMsg($content = '')
    {
        $textxml = "<xml><ToUserName><![CDATA[{$this->msgObj->FromUserName}]]></ToUserName><FromUserName><![CDATA[{$this->msgObj->ToUserName}]]></FromUserName><CreateTime>" . time () . "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>";
        // 做搜索处理
        if (empty ( $content ))
        {
            $content = "查询功能正在开发中...";
        }
        $resultstr = sprintf ( $textxml, $content );
        echo $resultstr;
    }

    /**
     * 回复图文消息
     */
    private function picMsg($data)
    {
        $newsxml = "<xml><ToUserName><![CDATA[{$this->msgObj->FromUserName}]]></ToUserName><FromUserName><![CDATA[{$this->msgObj->ToUserName}]]></FromUserName><CreateTime>" . time () . "</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>1</ArticleCount><Articles>%s</Articles></xml>";
        $itemxml = "";
        $itemxml .= "<item>";
        $itemxml .= "<Title><![CDATA[{$data['title']}]]></Title><Description><![CDATA[{$data['summary']}]]></Description><PicUrl><![CDATA[{$data['img']['url']}]]></PicUrl><Url><![CDATA[{$data['url']}]]></Url>";
        $itemxml .= "</item>";
        $resultstr = sprintf ( $newsxml, $itemxml );
        echo $resultstr;
    }

    /**
     * @param $openid
     * 根据openID获取access_token
     * 缓存如果有access_token 直接进行使用
     * 没有了进行获取
     * 得到access_token 通过openid和access_token获取用户信息
     * @return 用户ID
     */
    private function createUser($openid)
    {
        $token = Cache::get('wechat_access_token');
        if(!$token)
        {
            $access_token = $this->getAccessToken();
        }
        else{
            $access_token = $token;
        }
        $userInfo = $this->getUserInfo($access_token,$openid);
        $create['openid'] = $openid;
        $create['nickname'] = $userInfo['nickname'];
        $create['name'] = $userInfo['nickname'];
        $create['city'] = $userInfo['city'];
        $create['province'] = $userInfo['province'];
        $create['sex'] = $userInfo['sex'];
        $create['headimgurl'] = $userInfo['headimgurl'];
        UserModel::create($create);
    }

    //生成一个随机字符串
    public function generate_password( $length = 8 )
    {
        // 密码字符集，可任意添加你需要的字符
        $chars = 'abcdefghijklmnopqrstuvwxyz';
        $password = "";
        for ( $i = 0; $i < $length; $i++ )
        {
            // 这里提供两种字符获取方式
            // 第一种是使用 substr 截取$chars中的任意一位字符；
            // 第二种是取字符数组 $chars 的任意元素
            // $password .= substr($chars, mt_rand(0, strlen($chars) – 1), 1);
            $password .= $chars[ mt_rand(0, strlen($chars) - 1) ];
        }
        return $password;
    }

    /**
     * @param $openid
     * 获取accessToken
     */
    private function getAccessToken()
    {
        $wxResult = curl_get($this->wxAccessUrl);
        if(empty($wxResult))
        {
            $this->textMsg('请取消关注后再进行关注');
        }
        else{
            $loginFail = array_key_exists('errcode',$wxResult);
            if($loginFail)
            {
                $this->textMsg($wxResult['errmsg']);
            }
            else{
                $expire_in = config('setting.access_token_expire_in');
                $result = cache('wechat_access_token',$wxResult['access_token'],$expire_in);
                if(!$result)
                {
                    $this->textMsg('请取消关注后再进行关注');
                }
                return $wxResult['access_token'];
            }
        }
    }
    /**
     * 获取用户信息
     */
    private function getUserInfo($access_token,$openid)
    {
        $userInfoUrl = sprintf(config('wechat.user_url'),
            $access_token,$openid);
        $userInfo = curl_get($userInfoUrl);
        if(empty($userInfo))
        {
            $this->textMsg('请取消关注后再进行关注');
        }
        else{
            $loginFail = array_key_exists('errcode',$userInfo);
            if($loginFail)
            {
                $this->textMsg('用户信息添加失败');
            }
            else{
                return $userInfo;
            }
        }
    }
}